#include "D2ModSys14d.h"
#include <direct.h>
#include <stdio.h>

#include "cJSON.h"

#define MODE_GS FALSE
#define MODE_NORMAL TRUE

class TCustomDll
{
public:
    union
    {
        HMODULE module;
        DWORD offset;
    };
    TCustomDll *nextDll;
    void initialize(DWORD offsetDll);
    void init();
    void release();

private:
    void(__stdcall *initFct)();
    void(__stdcall *releaseFct)();
    struct ModModule *(__stdcall *getInfoFct)();
};

TCustomDll *customDlls = NULL;

void TCustomDll::initialize(DWORD offsetDll)
{
    offset = offsetDll;

    initFct = (void(__stdcall *)())GetProcAddress(module, (LPCSTR) "_Init@0");
    if (!initFct)
        initFct = (void(__stdcall *)())GetProcAddress(module, (LPCSTR)10000);

    releaseFct = (void(__stdcall *)())GetProcAddress(module, (LPCSTR) "_Release@0");
    if (!releaseFct)
        releaseFct = (void(__stdcall *)())GetProcAddress(module, (LPCSTR)10001);

    getInfoFct = (struct ModModule * (__stdcall *)()) GetProcAddress(module, (LPCSTR) "_GetInfo@0");
    if (!getInfoFct)
        getInfoFct = (struct ModModule * (__stdcall *)()) GetProcAddress(module, (LPCSTR)10002);
}

void TCustomDll::init()
{
    if (initFct)
        initFct();
};

void TCustomDll::release()
{
    if (releaseFct)
        releaseFct();
};

static void create_internal()
{
    FILE *f;
    long len;
    char cwd[_MAX_PATH];
    char config_file[_MAX_PATH];
    cJSON *Root;
    char *data;
    TCustomDll* nextDll;
    DWORD offset_currentDll;

    _getcwd(cwd, MAX_PATH);
    if (cwd[strlen(cwd)] != '\\')
        strcat(cwd, "\\");

    sprintf(config_file,"%s%s",cwd,"D2ModSys14d.json");
    f = fopen(config_file, "rb");

    if (!f)
        goto exit_error;

    fseek(f, 0, SEEK_END);
    len = ftell(f);
    fseek(f, 0, SEEK_SET);

    data = (char *)malloc(len + 1);
    if (!data)
        goto exit_error;
    fread(data, 1, len, f);
    Root = cJSON_Parse(data);
    if (!Root)
    {
        fclose(f);
        free(data);
        goto exit_error;
    }
    fclose(f);
    free(data);

    if (Root->type != cJSON_Array)
        goto exit_error;

    for (int i = 0; i < cJSON_GetArraySize(Root); i++)
    {
        cJSON *object = cJSON_GetArrayItem(Root, i);
        if (!object || object->type != cJSON_String)
        {
            goto exit_error;
        }
        else
        {
            sprintf(config_file,"%s%s//%s.dll",cwd,MOD_SCRIPTS_PATH,object->valuestring);
            offset_currentDll = (DWORD)LoadLibrary(config_file);
            if (!offset_currentDll)
            {
                goto exit_error;
            }
            nextDll = customDlls;
            customDlls = new (TCustomDll);
            customDlls->nextDll = nextDll;
            customDlls->initialize(offset_currentDll);
        }
    }

    return;
exit_error:
    MessageBoxA(NULL, "Json dll Failed", "Error", MB_OK);
    return;
}

extern "C" __declspec(dllexport) BOOL __stdcall PatchCreate(HMODULE handle)
{
    create_internal();

    TCustomDll *dll = customDlls;
    while (dll)
    {
        dll->init();
        dll = dll->nextDll;
    }
    HMODULE gs_handle =LoadLibraryA(MOD_GS_PATH);
    if(gs_handle)
    {
        auto gsmain_Fct = (void(__stdcall *)(void* ptr))GetProcAddress(gs_handle, (LPCSTR) "_GsMain@4");
        if(gsmain_Fct)
        {
            gsmain_Fct(NULL);
            return MODE_GS;
        }
    }
    return MODE_NORMAL;
}

extern "C" __declspec(dllexport) void __stdcall PatchDestory()
{
    if (!customDlls)
        return;

    TCustomDll *dll = customDlls;
    TCustomDll *nextDll;
    while (dll)
    {
        dll->release();
        if (dll->offset)
            FreeLibrary((HMODULE)dll->offset);
        nextDll = dll->nextDll;
        delete (dll);
        dll = nextDll;
    }
    return;
}